/**
 * GcmServer
 * GcmMessage.java
 * 
 * Copyright 2013 Teachizen
 * Written by cloudstick
 *	2013. 8. 4. first written.
 *
 * note:
 * 		Copy from google gcm server implementation.
 * 		- com.google.android.gcm.server.Message.java 
 */
package com.bnsworks.server.push.gcm.message;

import static com.bnsworks.server.push.gcm.GcmConstants.PARAM_COLLAPSE_KEY;
import static com.bnsworks.server.push.gcm.GcmConstants.PARAM_DELAY_WHILE_IDLE;
import static com.bnsworks.server.push.gcm.GcmConstants.PARAM_DRY_RUN;
import static com.bnsworks.server.push.gcm.GcmConstants.PARAM_PAYLOAD_PREFIX;
import static com.bnsworks.server.push.gcm.GcmConstants.PARAM_REGISTRATION_ID;
import static com.bnsworks.server.push.gcm.GcmConstants.PARAM_RESTRICTED_PACKAGE_NAME;
import static com.bnsworks.server.push.gcm.GcmConstants.PARAM_TIME_TO_LIVE;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.ObjectMapper;

import com.bnsworks.server.push.gcm.GcmException;

import lombok.Data;

/**
 * GCM message.
 *
 * <p>
 * Instances of this class are immutable and should be created using a
 * {@link Builder}. Examples:
 *
 * <strong>Simplest message:</strong>
 * <pre><code>
 * GcmMessage message = new GcmMessage.Builder().build();
 * </pre></code>
 *
 * <strong>Message with optional attributes:</strong>
 * <pre><code>
 * GcmMessage message = new GcmMessage.Builder()
 *    .collapseKey(collapseKey)
 *    .timeToLive(3)
 *    .delayWhileIdle(true)
 *    .dryRun(true)
 *    .restrictedPackageName(restrictedPackageName)
 *    .build();
 * </pre></code>
 *
 * <strong>Message with optional attributes and payload data:</strong>
 * <pre><code>
 * GcmMessage message = new GcmMessage.Builder()
 *    .collapseKey(collapseKey)
 *    .timeToLive(3)
 *    .delayWhileIdle(true)
 *    .dryRun(true)
 *    .restrictedPackageName(restrictedPackageName)
 *    .addData("key1", "value1")
 *    .addData("key2", "value2")
 *    .build();
 * </pre></code>
 */
@SuppressWarnings("serial")
@Data
public final class GcmMessage implements Serializable {

	private String collapse_key;
	private Boolean delay_while_idle;
	private Integer time_to_live;
	private Map<String, String> data;
	private Boolean dry_run;
	private String restricted_package_name;
	private List<String> registration_ids;
	
	public String getJsonMessage(List<String> registration_ids) {
		
		this.setRegistration_ids(registration_ids);
		
		ObjectMapper mapper = new ObjectMapper();
	    try {
			return mapper.writeValueAsString(this);
		} catch (Exception e) {
			throw new GcmException("Exception occured whiling mapping json message.", e);
		}
	}
	
	public String getQueryMessage(String registrationId) {
		
		StringBuilder body = newBody(PARAM_REGISTRATION_ID, registrationId);
		if ( delay_while_idle != null ) {
			addParameter(body, PARAM_DELAY_WHILE_IDLE, delay_while_idle ? "1" : "0");
		}
		if ( dry_run != null ) {
			addParameter(body, PARAM_DRY_RUN, dry_run ? "1" : "0");
		}
	    if ( collapse_key != null ) {
	    	addParameter(body, PARAM_COLLAPSE_KEY, collapse_key);
	    }
	    if ( restricted_package_name != null ) {
	    	addParameter(body, PARAM_RESTRICTED_PACKAGE_NAME, restricted_package_name);
	    }
	    if ( time_to_live != null ) {
	    	addParameter(body, PARAM_TIME_TO_LIVE, Integer.toString(time_to_live));
	    }
	    
	    for (Entry<String, String> entry : data.entrySet()) {
	    	String key = entry.getKey();
	    	String value = entry.getValue();
	    	if (key == null || value == null) {
	    		continue;
	    	} else {
	    		key = PARAM_PAYLOAD_PREFIX/* data. */ + key;
	    		try {
					addParameter(body, key, URLEncoder.encode(value, "UTF-8"));
				} catch (UnsupportedEncodingException e) {
					throw new GcmException("UnsupportedEncodingException: ", e);
				}
	    	}
	    }
	    return body.toString();
	}
	
	private static StringBuilder newBody(String name, String value) {
  		return new StringBuilder(name).append('=').append(value);
  	}
	
	protected static void addParameter(StringBuilder body, String name, String value) {
  		body.append('&').append(name).append('=').append(value);
  	}
}
